package com.ghsc.ghxc.jsb.utils

import android.content.Context
import android.os.Handler
import android.os.Looper
import android.util.Log
import com.alibaba.sdk.android.oss.*
import com.alibaba.sdk.android.oss.callback.OSSCompletedCallback
import com.alibaba.sdk.android.oss.callback.OSSProgressCallback
import com.alibaba.sdk.android.oss.common.OSSLog
import com.alibaba.sdk.android.oss.common.auth.OSSCredentialProvider
import com.alibaba.sdk.android.oss.common.auth.OSSStsTokenCredentialProvider
import com.alibaba.sdk.android.oss.internal.OSSAsyncTask
import com.alibaba.sdk.android.oss.model.PutObjectRequest
import com.alibaba.sdk.android.oss.model.PutObjectResult
import com.cn.appcore.ext.executeResponse
import com.cn.appcore.ext.loge
import com.cn.appcore.ext.showToast
import com.cn.appcore.utils.TimeUtils
import com.ghsc.ghxc.jsb.api.ApiMarket
import com.ghsc.ghxc.jsb.config.UserManager
import com.ghsc.ghxc.jsb.ui.my.bean.OssTokenResultDataBean
import kotlinx.coroutines.*

class OSSUtil {

    companion object {

        private var oss: OSS? = null
        private var bucketName: String? = null
        private var rootPATH: String = ""

        //单文件上传
        private var currentUploadFilePath: String? = null
        private var uploadFileCallback: UploadFileCallback? = null

        //字节流上传
        private var currentUploadByteArray: ByteArray? = null

        //多文件上传
        private var currentUploadFilePathList: ArrayList<String> = ArrayList()

        private var currentUploadFileCount = 1
        private var currentUploadPosition = 1

        private var currentUrlList = ArrayList<String>()
        private var uploadMultiFilesCallback: UploadMultiFilesCallback? = null

        private val mHandler = Handler(Looper.myLooper()!!)

        /**
         * 初始化服务
         */
        fun prepare(context: Context, initCallback : ()->Unit) {
            if(oss == null){
                OSSLog.logInfo("oss init")
                onOssDataMethod{
                    OSSLog.logInfo("oss getInfo success")
                    bucketName = it.bucketName
                    rootPATH = it.rootPATH
                    //该配置类如果不设置，会有默认配置，具体可看该类
                    val conf = ClientConfiguration()
                    conf.connectionTimeout = 15 * 1000 // 连接超时，默认15秒
                    conf.socketTimeout = 15 * 1000 // socket超时，默认15秒
                    conf.maxConcurrentRequest = 5 // 最大并发请求数，默认5个
                    conf.maxErrorRetry = 2 // 失败后最大重试次数，默认2次
                    OSSLog.enableLog() //这个开启会支持写入手机sd卡中的一份日志文件位置在SDCard_path\OSSLog\logs.csv
                    val credentialProvider: OSSCredentialProvider = OSSStsTokenCredentialProvider(
                        it.AccessKeyId,
                        it.AccessKeySecret,
                        it.SecurityToken
                    )
                    oss = OSSClient(context, it.ossHost, credentialProvider, conf)
                    initCallback()
                }
            }else{
                initCallback()
            }
        }

        //避免多次回调
        private var lastProgress = 0

        /**
         * 上传单个文件
         *
         */
        fun upLoadFile(
            context: Context,
            uploadFilePath: String,
            type:String,
            uploadFileCallback: UploadFileCallback,
            progressCallback: (percent: Int) -> Unit = {}
        ) {
            lastProgress = 0
            currentUploadFilePath = uploadFilePath
            this.uploadFileCallback = uploadFileCallback

            prepare(context){
                OSSLog.logInfo("upLoadFile start")
                // 构造上传请求
                val userId = UserManager.getUserId()
                val objectKey= when {
                    uploadFilePath.contains(".mp4") -> {
                        rootPATH + type + StringUtil.getTime()+"/"+ userId +"/"+ TimeUtils.getTime10()+(0..100).random()+".mp4"
                    }
                    uploadFilePath.contains(".mp3") -> {
                        rootPATH + type + StringUtil.getTime()+"/"+ userId +"/"+ TimeUtils.getTime10()+(0..100).random()+".mp3"
                    }
                    else ->  rootPATH + type + StringUtil.getTime()+"/"+ userId +"/"+ TimeUtils.getTime10()+(0..100).random()+".png"
                }
                val put = PutObjectRequest(bucketName, objectKey, uploadFilePath)
                // 异步上传时可以设置进度回调
                put.progressCallback =
                    OSSProgressCallback { request, currentSize, totalSize ->
                        val prog = (currentSize.toFloat() / totalSize * 100).toInt()
                        if (lastProgress != prog) {
                            lastProgress = prog
                            mHandler.post{
                                progressCallback(prog)
                            }
                        }
                    }
                //开始上传
                val task: OSSAsyncTask<*> = oss!!.asyncPutObject(
                    put,
                    object : OSSCompletedCallback<PutObjectRequest?, PutObjectResult?> {
                        override fun onSuccess(request: PutObjectRequest?, result: PutObjectResult?) {
                            currentUploadFilePath = null
                            val url = oss!!.presignPublicObjectURL(bucketName, objectKey)
                            OSSLog.logInfo("onSuccess url = $url")
                            mHandler.post {
                                uploadFileCallback.onUploadSuccess(url)
                            }
                        }

                        override fun onFailure(
                            request: PutObjectRequest?,
                            clientExcepion: ClientException,
                            serviceException: ServiceException
                        ) {
                            currentUploadFilePath = null
                            mHandler.post {
                                uploadFileCallback.onUploadFailure()
                            }
                            // 请求异常
                            OSSLog.logError("onFailure = $clientExcepion")
                            // 服务异常
                            OSSLog.logError("onFailure = $serviceException")
                        }
                    })
            }
        }

        /**
         * 上传多文件
         *
         */
        fun upLoadMultiFiles(
            context: Context,
            uploadFilePathList: ArrayList<String>,
            type:String,
            uploadMultiFilesCallback: UploadMultiFilesCallback,
            progressCallback: (current: Int, all: Int) -> Unit = { _, _ -> {} }
        ) {

            this.uploadMultiFilesCallback = uploadMultiFilesCallback

            currentUploadFilePathList.clear()
            currentUploadFilePathList.addAll(uploadFilePathList)

            currentUploadFileCount = uploadFilePathList.size
            currentUploadPosition = 1

            //清空数据
            currentUrlList.clear()
            prepare(context){
                OSSLog.logInfo("upLoadMultiFiles start")
                //递归上传
                if (currentUploadFilePathList.size > 0) {
                    upLoadFileRecursion(currentUploadFilePathList.removeAt(0),type, progressCallback)
                } else {
                    //全部上传完成
                    mHandler.post {
                        uploadMultiFilesCallback.onUploadSuccess(currentUrlList)
                    }
                }
            }
        }


        /**
         * 递归上传
         */
        private fun upLoadFileRecursion(
            uploadFilePath: String,
            type:String,
            progressCallback: (current: Int, all: Int) -> Unit = { _, _ -> {} }
        ) {
            OSSLog.logInfo("upLoadFileRecursion start")
            currentUploadPosition = currentUploadFileCount - currentUploadFilePathList.size
            //进度, 走handler不更新
            progressCallback(currentUploadPosition, currentUploadFileCount)

            //上传文件的名称格式   反转时间戳+userId.文件格式
            val userId = UserManager.getUserId()
            val objectKey= when {
                uploadFilePath.contains(".mp4") -> {
                    rootPATH + type + StringUtil.getTime()+"/"+ userId +"/" + TimeUtils.getTime10()+(0..100).random()+".mp4"
                }
                uploadFilePath.contains(".mp3") -> {
                    rootPATH + type + StringUtil.getTime()+"/"+ userId +"/" + TimeUtils.getTime10()+(0..100).random()+".mp3"
                }
                else ->  rootPATH + type + StringUtil.getTime()+"/"+ userId +"/" + TimeUtils.getTime10()+(0..100).random()+".png"
            }
            // 构造上传请求
            val put = PutObjectRequest(bucketName, objectKey, uploadFilePath)
            put.progressCallback =
                OSSProgressCallback { request, currentSize, totalSize ->
                    loge("currentSize: $currentSize  ===  totalSize: $totalSize")
                }
            //开始上传
            val task: OSSAsyncTask<*> = oss!!.asyncPutObject(
                put,
                object : OSSCompletedCallback<PutObjectRequest?, PutObjectResult?> {
                    override fun onSuccess(request: PutObjectRequest?, result: PutObjectResult?) {
                        val url = oss!!.presignPublicObjectURL(bucketName, objectKey)
                        OSSLog.logInfo("upLoadFileRecursion url = $url")
                        //将地址添加进去
                        currentUrlList.add(url)
                        //再次调用自身
                        if (currentUploadFilePathList.size > 0) {
                            upLoadFileRecursion(currentUploadFilePathList.removeAt(0),type)
                        } else {
                            //全部上传完成
                            mHandler.post {
                                uploadMultiFilesCallback?.onUploadSuccess(currentUrlList)
                            }
                        }
                    }
                    override fun onFailure(
                        request: PutObjectRequest?,
                        clientExcepion: ClientException,
                        serviceException: ServiceException
                    ) {
                        // 上传失败
                        mHandler.post {
                            uploadMultiFilesCallback?.onUploadFailure()
                        }
                        // 请求异常
                        OSSLog.logError("onFailure = $clientExcepion")
                        // 服务异常
                        OSSLog.logError("onFailure = $serviceException")
                    }
                })
        }

        /**
         * 获取oss配置信息
         */
        private fun onOssDataMethod(result: (bean: OssTokenResultDataBean)->Unit) {
            launch {
                val response = withContext(Dispatchers.IO) {
                    ApiMarket.service.onAliYunOosData("")
                }
                executeResponse(
                    response,
                    { result.invoke(response.data) },
                    { loge("暂未登录") }
                ) {
                    loge("oss getInfo failure")
                    showToast("OSS服务初始化失败")
                    uploadFileCallback?.onUploadFailure()
                    uploadMultiFilesCallback?.onUploadFailure()
                }
            }
        }

        private fun launch(tryBlock: suspend CoroutineScope.() -> Unit) {
            launchOnUI {
                tryCatch(tryBlock, {}, {}, true)
            }
        }

        private fun launchOnUI(block: suspend CoroutineScope.() -> Unit) {
            MainScope().launch { block() }
            // viewModelScope.launch { block() }  /// ???
        }

        private suspend fun tryCatch(
            tryBlock: suspend CoroutineScope.() -> Unit,
            catchBlock: suspend CoroutineScope.(Throwable) -> Unit,
            finallyBlock: suspend CoroutineScope.() -> Unit,
            handleCancellationExceptionManually: Boolean = false
        ) {
            coroutineScope {
                try {
                    tryBlock()
                } catch (e: Exception) {
                    loge("网络请求异常日志：${e.localizedMessage}")
                    if (e !is CancellationException || handleCancellationExceptionManually) {
                        catchBlock(e)
                    } else {
                        throw e
                    }
                } finally {
                    finallyBlock()
                }
            }
        }
    }



    interface UploadFileCallback {

        fun onUploadSuccess(url: String)

        fun onUploadFailure()
    }

    interface UploadMultiFilesCallback {

        fun onUploadSuccess(urlList: ArrayList<String>)

        fun onUploadFailure()
    }
}